﻿#pragma once

#include "../RHIResource.h"

class RHIBuffer : public RHIResource
{
public:

	virtual ~RHIBuffer();


	VkBufferCreateFlags GetBufferCreateFlags() const;
	VkDeviceSize GetSize() const;
	VkBufferUsageFlags GetUsage() const;
	VkSharingMode GetSharingMode() const;
	std::vector<VkQueueFlagBits> GetQueueFamilys() const;
	bool IsInQueueFamily(VkQueueFlagBits queueFlag) const;
	VkMemoryPropertyFlags GetMemoryProperty() const;

	bool IsMultiple() const;

	VkBuffer GetBuffer(uint32 index = UINT32_MAX) const;
	VkDeviceMemory GetBufferMemory(uint32 index = UINT32_MAX) const;


	void WriteAllMemory(void* buffer, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE);

	// 只有 Host Visible 而不指定 Coherent，当 CPU 更改数据时则需要手动调用 vkFlushMappedMemoryRanges 函数将写入的数据 Flush 到设备使设备可见，当 GPU完成后需要手动调用 vkInvalidateMappedMemoryRanges 使设备写入的数据使主机可见
	void WriteMemory(void* buffer, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE, uint32 index = UINT32_MAX);
	
	void ReadMemory(void* buffer, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE, uint32 index = UINT32_MAX);

	VkDeviceAddress GetDeviceAddress() const;


protected:

	uint32 GetFrameCount() const;

	uint32 GetFrameIndex() const;

protected:

	bool mIsMultiple;

	// VkBufferCreateInfo
	VkBufferCreateFlags mFlags;
	VkDeviceSize mSize;
	VkBufferUsageFlags mUsage;
	VkSharingMode mSharingMode;
	std::vector<VkQueueFlagBits> mQueueFamilys;

	// Memery alloc
	VkMemoryPropertyFlags mMemoryProperty;

	std::vector<VkBuffer> mBuffers;
	std::vector<VkDeviceMemory> mBufferMemorys;
};



#if 0

//void Transition(
//	VkCommandBuffer commandBuffer,
//	VkAccessFlags srcAccessMask,
//	VkAccessFlags dstAccessMask,
//	VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
//	VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT,
//	VkDeviceSize offset = 0, 
//	VkDeviceSize size = VK_WHOLE_SIZE
//);

//void RHIBuffer::Transition(VkCommandBuffer commandBuffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStageMask /*= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT*/, VkPipelineStageFlags dstStageMask /*= VK_PIPELINE_STAGE_TRANSFER_BIT*/, VkDeviceSize offset /*= 0*/, VkDeviceSize size /*= VK_WHOLE_SIZE */)
//{
//	VkBufferMemoryBarrier bufferMemoryBarrier;
//	bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
//	bufferMemoryBarrier.srcAccessMask = srcAccessMask;
//	bufferMemoryBarrier.dstAccessMask = dstAccessMask;
//
//	bufferMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
//	bufferMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
//
//	bufferMemoryBarrier.buffer = mBuffer;
//	bufferMemoryBarrier.offset = offset;
//	bufferMemoryBarrier.size = size;
//
//	vkCmdPipelineBarrier(
//		commandBuffer,
//		srcStageMask,
//		dstStageMask,
//		0,
//		0, nullptr,
//		0, &bufferMemoryBarrier,
//		0, nullptr
//	);
//}

void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory, VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE);

void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);

void RHIBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory, VkSharingMode sharingMode)
{
	VkBufferCreateInfo bufferCreateInfo{};
	bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
	bufferCreateInfo.size = size;
	bufferCreateInfo.usage = usage;
	bufferCreateInfo.sharingMode = sharingMode;

	VK_CHECK_RESULT(vkCreateBuffer(GVulkanRHI->GetLogicalDevice(), &bufferCreateInfo, nullptr, &buffer));

	VkMemoryRequirements memRequirements;
	vkGetBufferMemoryRequirements(GVulkanRHI->GetLogicalDevice(), buffer, &memRequirements);

	VkMemoryAllocateInfo memoryAllocateInfo{};
	memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
	memoryAllocateInfo.allocationSize = memRequirements.size;

	memoryAllocateInfo.memoryTypeIndex = GVulkanRHI->FindMemoryType(memRequirements.memoryTypeBits, properties);

	VK_CHECK_RESULT(vkAllocateMemory(GVulkanRHI->GetLogicalDevice(), &memoryAllocateInfo, nullptr, &bufferMemory));

	vkBindBufferMemory(GVulkanRHI->GetLogicalDevice(), buffer, bufferMemory, 0);
}

void RHIBuffer::CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
{
	VkCommandBuffer commandBuffer = GRenderState->BeginSingleCommand();

	VkBufferCopy copyRegion{};
	copyRegion.srcOffset = 0;
	copyRegion.dstOffset = 0;
	copyRegion.size = size;
	vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

	GRenderState->FlushSingleCommand(commandBuffer);
}

void CopyFrom(RefCountPtr<RHIBuffer> srcBuffer);

void RHIBuffer::CopyFrom(RefCountPtr<RHIBuffer> srcBuffer)
{
	assert(srcBuffer.IsValid());
	assert(srcBuffer->GetSize() == mSize);
	CopyBuffer(srcBuffer->GetBuffer(), mBuffer, mSize);
}


#endif